/*
============================================================================
 Name        : dd_mad.c
 Author      : Jian
 Version     :
 Copyright   : GPL
 Description : dd_mad.c - source file
============================================================================
*/

/* INCLUDE FILES */
#include "dd_mad.h"
#include "dd_log.h"

dd_mad* dd_mad_create() 
{
    dd_mad* mad = dd_malloc(sizeof(dd_mad)) ;

    __DD_CHECK_MEM(mad, sizeof(dd_mad), return NULL) ;

    mad_stream_init(&mad->stream);
    mad_frame_init(&mad->frame);
    mad_synth_init(&mad->synth);

    return mad ;
}

void dd_mad_destroy(dd_mad* mad) 
{
    __DD_ASSERT_DEFENSE(mad, return) ;

    mad_synth_finish(&mad->synth);
    mad_frame_finish(&mad->frame);
    mad_stream_finish(&mad->stream);

    dd_free(mad) ;
}

static __dd_inline short _mad_fix_to_short(mad_fixed_t fixed)
{
	/* A fixed point number is formed of the following bit pattern:
	 *
	 * SWWWFFFFFFFFFFFFFFFFFFFFFFFFFFFF
	 * MSB                          LSB
	 * S ==> Sign (0 is positive, 1 is negative)
	 * W ==> Whole part bits
	 * F ==> Fractional part bits
	 *
	 * This pattern contains MAD_F_FRACBITS fractional bits, one
	 * should alway use this macro when working on the bits of a fixed
	 * point number. It is not guaranteed to be constant over the
	 * different platforms supported by libmad.
	 *
	 * The signed short value is formed, after clipping, by the least
	 * significant whole part bit, followed by the 15 most significant
	 * fractional part bits. Warning: this is a quick and dirty way to
	 * compute the 16-bit number, madplay includes much better
	 * algorithms.
	 */

	/* Clipping */
	if(fixed >= MAD_F_ONE)
		return (SHRT_MAX);

	if(fixed <= -MAD_F_ONE)
		return (-SHRT_MAX);

	/* Conversion. */
	fixed = fixed >> (MAD_F_FRACBITS-15);
	return ((short)fixed);
}

int dd_mad_decode(dd_mad* mad, const DD_BYTE* input, DD_UINT inputLen, DD_BYTE* output, DD_UINT* outputLen) 
{
    int      maxOutputLen = 0 ;
    int      i = 0;
    short    sample = 0;
    DD_BYTE* outputPtr = output ;

    __DD_ASSERT_DEFENSE(mad && input && inputLen > 0 && output && outputLen, return -1) ;
    __DD_ASSERT_DEFENSE(*outputLen >= (1152 * 2 * 2), return -1) ;  /* must bigger than 1152 (max sample per frame) * 2(max channels: 2) * 2(16 bit pcm output) */

    maxOutputLen = *outputLen ;
    *outputLen = 0 ;
    
    mad_stream_buffer(&mad->stream, input, inputLen);
    mad->stream.error = 0 ;

    mad_timer_reset(&mad->timeCount) ;

    while(1) {
    
        if(__unlikely(mad_frame_decode(&mad->frame, &mad->stream) != 0)) {
            
            dd_log_trace("frame decode got error: %d, %s", mad->stream.error, mad_stream_errorstr(&mad->stream)) ;

			if(MAD_RECOVERABLE(mad->stream.error))
				continue;

            if (__likely(mad->stream.error == MAD_ERROR_BUFLEN)) 
                return (mad->stream.next_frame > input) ? (mad->stream.next_frame - input) : EMadBufferIncomplete ;
            
            dd_log_error("frame decode got unrecoverable error") ;
            return -1 ;
		}
        
        mad_timer_add(&mad->timeCount, mad->frame.header.duration) ;
        mad_synth_frame(&mad->synth, &mad->frame);

        if (__unlikely(mad->samplePerFrame == 0)) {
            mad->samplePerFrame = mad->synth.pcm.length ;
            dd_log_trace("got samplePerFrame: %d in frame", mad->samplePerFrame) ;
            
            __DD_ASSERT_DEFENSE(mad->samplePerFrame > 0 && mad->samplePerFrame < 1152, dd_log_error("bad samplePerFrame: %d in frame", mad->samplePerFrame); return -1) ;
        }
        
        __DD_ASSERT_DEFENSE(mad->samplePerFrame == mad->synth.pcm.length, 
            dd_log_error("samplePerFrame: %d is not equal to synth.length: %d in frame", mad->samplePerFrame, mad->synth.pcm.length); return -1) ;

        for(i = 0; i < mad->samplePerFrame; i++) {
			/* Left channel */
			sample = _mad_fix_to_short(mad->synth.pcm.samples[0][i]);

			*(outputPtr++) = sample & 0xff;
			*(outputPtr++) = sample >> 8;

			/* Right channel. If the decoded stream is monophonic then
			 * the right output channel is the same as the left one.
			 */
			if(MAD_NCHANNELS(&(mad->frame.header)) == 2)
				sample = _mad_fix_to_short(mad->synth.pcm.samples[1][i]);

            *(outputPtr++) = sample & 0xff;
            *(outputPtr++) = sample >> 8;
		}

        if ((maxOutputLen - (outputPtr - output)) < (1152 * 2 * 2))
            break ;
    }
    
    *output = outputPtr - output ;
    __DD_ASSERT_DEFENSE((mad->stream.next_frame - input) > 0, return -1) ;
    return (mad->stream.next_frame - input) ;
}

DD_UINT dd_mad_add_time(dd_mad* mad, mad_timer_t* timeCounter) 
{
    __DD_ASSERT_DEFENSE(mad && timeCounter, return -1) ;
    mad_timer_add(timeCounter, mad->timeCount);
    return 0 ;
}


/* End of File */
